package fr.generali.sas.dao;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Component;

import fr.generali.sas.domain.FluxHibernate;
import fr.generali.sas.model.v1_0.Flux;

/**
 * 
 * classe responsable de l'acces au flux
 * 
 * @author a000eqm
 * 
 */

@Component("fluxJdbcDao")
public class FluxJdbcDao implements InitializingBean, FluxInternalDao {

	@Autowired
	SimpleJdbcTemplate jdbcTemplate;
	
	@Value("${row.limit}")
	int rowLimit = 100;

	private static Logger logger = LoggerFactory.getLogger(FluxJdbcDao.class);

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {

		logger.info("SET...");
	}

	/**
	 * Pour une sycnhro Agence, reserve les flux avant envoi
	 * 
	 * @param codeIntermediaire
	 *            code intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de synchro
	 * @return nombre de lignes mises à jour
	 */
	public int reserveFluxPourSynchroAgence(String codeIntermediaire,
			String numeroReservation) {
		String sql = "update FLUX set NUMRESERVATION = ? , DATEENVOI = sysdate where MODEENVOI is null and NUMRESERVATION is null and CDEINTERMEDIAIRE = ?";
		int updates = jdbcTemplate.update(sql, new Object[] {
				numeroReservation, codeIntermediaire });

		return updates;
	}

	/**
	 * Pour une sycnhro client, reserve les flux avant envoi
	 * 
	 * @param codeIntermediaire
	 *            code intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de synchro
	 * @param idClient
	 *            identifiant client
	 * @return nombre de lignes mises à jour
	 */
	public int reserveFluxPourSynchroClient(String codeIntermediaire,
			String numeroReservation, String idClient) {
		String sql = "update FLUX set NUMRESERVATION = ? , DATEENVOI = sysdate where MODEENVOI is null and NUMRESERVATION is null and CDEINTERMEDIAIRE = ? and IDCLIENT = ?";
		int updates = jdbcTemplate.update(sql, new Object[] {
				numeroReservation, codeIntermediaire, idClient });

		return updates;
	}

	/**
	 * Retourne la liste de flux reservé lors d'une demande de synchro de type Client
	 * 
	 * @param codeIntermediaire
	 *            code de l'intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de flux
	 * @return
	 */
	public List<Flux> getFluxPourSynchroClient(String codeIntermediaire,
			String numeroReservation) {
		List<Flux> lstFlux = getFLuxPourSynchro(codeIntermediaire,
				numeroReservation);

		return lstFlux;
	}

	/**
	 * Retourne la liste de flux reservé lors d'une demande de synchro de type Agence
	 * 
	 * @param codeIntermediaire
	 *            code de l'intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de flux
	 * @return
	 */
	public List<Flux> getFluxPourSynchroAgence(String codeIntermediaire,
			String numeroReservation) {
		List<Flux> lstFlux = getFLuxPourSynchro(codeIntermediaire,
				numeroReservation);

		return lstFlux;
	}

	/**
	 * Retourne la liste de flux reservé lors d'une demande de synchro
	 * 
	 * @param codeIntermediaire
	 *            code de l'intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de flux
	 * @return
	 */
	private List<Flux> getFLuxPourSynchro(String codeIntermediaire,
			String numeroReservation) {
		String sql = "select IDCLIENT,CONTENT,DATEINSERTION,TYPEFLUX,ID from FLUX where NUMRESERVATION = :numeroReservation and CDEINTERMEDIAIRE = :codeIntermediaire order by ID";

		Map<String, String> args = new HashMap<String, String>();
		args.put("numeroReservation", numeroReservation);
		args.put("codeIntermediaire", codeIntermediaire);

//		List<Flux> lstFlux = jdbcTemplate.query(sql, new RowMapper<Flux>() {
//			public Flux mapRow(ResultSet rs, int rowNum) throws SQLException {
//				Flux f = new Flux();
//				f.setIdClient(rs.getString("IDCLIENT"));
//				f.setContent(rs.getString("CONTENT"));
//				f.setTimestamp(new Date(rs.getTimestamp("DATEINSERTION").getTime()));
//				f.setTypeFlux(rs.getString("TYPEFLUX"));
//
//				return f;
//			}
//		}, args);
		List<Flux> lstFlux = jdbcTemplate.query(sql, new FluxRowMapper(), args);
		return lstFlux;
	}

	/**
	 * @param codeIntermediaire
	 * @param numeroReservation
	 * @param statut
	 * @return
	 */
	public int acquitteIntegrationFlux(String codeIntermediaire,
			String numeroReservation, boolean statut) {

		if (statut)
			return acquitteSuccesIntegrationFlux(codeIntermediaire,
					numeroReservation);
		else
			return acquitteEchecIntegrationFlux(codeIntermediaire,
					numeroReservation);
	}
	
	/**
	 * Lors d'un succes d'intégration, valide la reservation des flux avec la date courante et le mode d'envoia TP (par opposition au mode batch [utile pour le HG juin 2011])
	 * 
	 * @param codeIntermediaire
	 *            code de l'intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de flux
	 * @return
	 */
	private int acquitteSuccesIntegrationFlux(String codeIntermediaire,
			String numeroReservation) {
		String sql ="update FLUX set MODEENVOI = 'TP' , DATEACK = sysdate where NUMRESERVATION = :numeroReservation and CDEINTERMEDIAIRE = :codeIntermediaire";
		Map<String, String> args = new HashMap<String, String>();
		args.put("numeroReservation", numeroReservation);
		args.put("codeIntermediaire", codeIntermediaire);
		
		int updates =  jdbcTemplate.update(sql, args);
		return updates;
	}
	
	/**
	 * Lors d'un echec d'intégration, supprime la reservation des flux pour autoriser le recyclage
	 * 
	* @param codeIntermediaire
	 *            code de l'intermediaire
	 * @param numeroReservation
	 *            identifiant de la demande de flux
	 * @return
	 */
	private int acquitteEchecIntegrationFlux(String codeIntermediaire,
			String numeroReservation) {
		String sql = "update FLUX set NUMRESERVATION = NULL , DATEENVOI = NULL where NUMRESERVATION = :numeroReservation and CDEINTERMEDIAIRE = :codeIntermediaire";
		Map<String, String> args = new HashMap<String, String>();
		args.put("numeroReservation", numeroReservation);
		args.put("codeIntermediaire", codeIntermediaire);
		
		int updates =  jdbcTemplate.update(sql, args);
		return updates;
	}

	/**
	 * @param codeIntermediaire
	 * @param numeroReservation
	 * @param rowLimit
	 * @return
	 */
	public List<Flux> getFluxPourSynchroAgenceWithRowLimit(
			String codeIntermediaire, String numeroReservation, int rowLimit) {
		List<Flux> listFlux = getFluxPourSynchroWithRowLimit(codeIntermediaire,
				numeroReservation, rowLimit);

		return listFlux;
	}

	/**
	 * @param codeIntermediaire
	 * @param numeroReservation
	 * @param rowLimit
	 * @return
	 */
	private List<Flux> getFluxPourSynchroWithRowLimit(String codeIntermediaire,
			String numeroReservation, int rowLimit) {
		List<Flux> listFlux = Collections.<Flux>emptyList();
		//String sql = "select IDCLIENT, CDEINTERMEDIAIRE, CONTENT, DATEINSERTION, TYPEFLUX, ID from (select IDCLIENT,CDEINTERMEDIAIRE,CONTENT,DATEINSERTION,TYPEFLUX,ID from FLUX where NUM_REQUEST = :numeroReservation and CDEINTERMEDIAIRE = :codeIntermediaire order by ID) t where rownum < :rowLimit ";
		String sql = "select top :rowLimit IDCLIENT, CDEINTERMEDIAIRE, CONTENT, DATEINSERTION, TYPEFLUX, ID from FLUX where NUMRESERVATION = :numeroReservation and CDEINTERMEDIAIRE = :codeIntermediaire order by ID ";
		Map<String, String> args = new HashMap<String, String>();
		args.put("numeroReservation", numeroReservation);
		args.put("codeIntermediaire", codeIntermediaire);
		args.put("rowLimit", String.valueOf(rowLimit));
		
		listFlux = jdbcTemplate.query(sql, new FluxRowMapper(), args);
		return listFlux;
	}

	/**
	 * @param codeIntermediaire
	 * @param numeroReservation
	 * @param rowLimit
	 * @return
	 */
	public List<Flux> getFluxPourSynchroClientWithRowLimit(
			String codeIntermediaire, String numeroReservation, int rowLimit) {
		List<Flux> listFlux = getFluxPourSynchroWithRowLimit(codeIntermediaire,
				numeroReservation, rowLimit);
		return listFlux;
	}

	/**{@inheritDoc}
	 * @see fr.generali.sas.dao.FluxInternalDao#getNextFluxPourSynchroClientWithRowLimit(java.lang.String, java.lang.String, int, int)
	 */
	public List<Flux> getNextFluxPourSynchroClientWithRowLimit(
			String codeIntermediaire, String numeroReservation, int rowLimit,
			int fromId) {
		List<Flux> listFlux = Collections.<Flux> emptyList();

		listFlux = getNextFluxPourSynchroWithRowLimit(codeIntermediaire,numeroReservation, rowLimit, fromId);
		return listFlux;
	}

	/**{@inheritDoc}
	 * @see fr.generali.sas.dao.FluxInternalDao#getNextFluxPourSynchroAgenceWithRowLimit(java.lang.String, java.lang.String, int, int)
	 */
	public List<Flux> getNextFluxPourSynchroAgenceWithRowLimit(
			String codeIntermediaire, String numeroReservation, int rowLimit,
			int fromId) {
		List<Flux> listFlux = Collections.<Flux> emptyList();

		listFlux = getNextFluxPourSynchroWithRowLimit(codeIntermediaire,numeroReservation, rowLimit, fromId);
		return listFlux;
	}
	
	/**
	 * @param codeIntermediaire
	 * @param numeroReservation
	 * @param rowLimit
	 * @param fromId
	 * @return
	 */
	private List<Flux> getNextFluxPourSynchroWithRowLimit(
			String codeIntermediaire, String numeroReservation, int rowLimit,
			int fromId) {
		List<Flux> listFlux;
		String sql = "select top :rowLimit IDCLIENT, CDEINTERMEDIAIRE, CONTENT, DATEINSERTION, TYPEFLUX, ID from FLUX where NUMRESERVATION = :numeroReservation and CDEINTERMEDIAIRE = :codeIntermediaire and id > :fromId order by ID ";
		
		Map<String, String> args = new HashMap<String, String>();
		args.put("numeroReservation", numeroReservation);
		args.put("codeIntermediaire", codeIntermediaire);
		args.put("rowLimit", String.valueOf(rowLimit));
		args.put("fromId", String.valueOf(fromId));
		
		listFlux = jdbcTemplate.query(sql, new FluxRowMapper(), args);
		
		return listFlux;
	}

	
	/**{@inheritDoc}
	 * @see fr.generali.sas.dao.FluxInternalDao#getNbFluxForReservation(java.lang.String, java.lang.String)
	 */
	public int getNbFluxForReservation(String codeIntermediaire, String numeroReservation) {
		
		int nbFlux = jdbcTemplate.queryForInt("select count(1) from FLUX where NUMRESERVATION = ? and CDEINTERMEDIAIRE = ? ", new Object[]{numeroReservation,codeIntermediaire});
		
		return nbFlux;
	}

	/**{@inheritDoc}
	 * @see fr.generali.sas.dao.FluxInternalDao#save(fr.generali.sas.domain.FluxHibernate)
	 */
	public FluxHibernate save(FluxHibernate f) throws Exception {
		String sql = "insert into FLUX (ID, DATEINSERTION, CDEINTERMEDIAIRE, CDEPORTEFEUILLE, IDCLIENT, IDSINISTRE, IDCONTRAT, EMETTEUR, TYPEFLUX) values( :id, :dateinsertion,  :cdeintermediaire, :cdeportefeuille, :idclient, :idsinistre, :idcontrat, :emetteur, :typeflux)";
		Map<String, Object> args = new HashMap<String, Object>();
		//TODO gestion de la date en mode jdbc HSQL et Oracle
		//args.put("dateinsertion", "CURRENT_TIMESTAMP");
		args.put("dateinsertion", new Timestamp(Calendar.getInstance().getTimeInMillis()));
		//args.put("dateinsertion", new Date());
		//TODO gestion de la version
		//args.put("version", "1");
		//TODO gestion de ID
		args.put("id", null);
		args.put("cdeintermediaire", f.getCodeIntermediaire());
		args.put("cdeportefeuille", f.getCodePortefeuille());
		args.put("idclient", f.getIdClient());
		args.put("idcontrat", f.getIdContrat());
		args.put("idsinistre", f.getIdSinistre());
		args.put("emetteur", f.getEmetteur());
		args.put("typeflux", f.getTypeFlux());
		int updates = -1;
		
		//DEBUG
		if (logger.isDebugEnabled()) {
			int test = jdbcTemplate.queryForInt("call identity()", new Object[] {});
			logger.debug("AVANT TEST value call identity():" + test);
			updates = jdbcTemplate.update(sql, args);
			test = jdbcTemplate.queryForInt("call identity()", new Object[] {});
			logger.debug("APRES TEST value call identity():" + test);
			@SuppressWarnings("unused")
			int lines = jdbcTemplate.queryForInt("select count(1) from FLUX where CDEINTERMEDIAIRE = ? and IDCLIENT = ? and NUMRESERVATION is null",
					new Object[] { f.getCodeIntermediaire(), f.getIdClient() });
		}
		
		if(updates!=1) throw new Exception("insertion erreur");
		
		return f;
	}

}
